package com.cloudfun.campusshare.code.generate;

import cn.hutool.core.util.XmlUtil;
import com.sun.org.apache.xerces.internal.dom.DeferredElementImpl;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 加载java文件类而非class文件
 * <p>
 * Created by Huqin on 2020/5/29
 */
public class JavaFileClassLoader extends ClassLoader {

    // TODO 动态获取这两个路径
    public static final String TARGET_FOLDER = "D:\\campusshare\\target\\classes\\";
    private static final String MAVEN_REPO = "D:\\develop\\apache-maven-3.6.3\\local-repo\\";

    private static final Map<String, Class> cache = new HashMap<>();

    private final String javacCpParams;

    public JavaFileClassLoader() {
        javacCpParams = String.join(";", resolveLib());
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (!name.startsWith("com.cloudfun") || name.contains("entity")) {
            return getClass().getClassLoader().loadClass(name);
        }
        if (cache.containsKey(name)) {
            return cache.get(name);
        }
        String javaFilePath = packageToPath(name);
        String absFilePath = CodeGenerator.SRC_CODE_FOLDER + javaFilePath + ".java";
        //动态编译
        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        int status = javac.run(null, null, null,
                "-sourcepath", CodeGenerator.SRC_CODE_FOLDER,
                "-nowarn",
                "-cp", javacCpParams,
                "-d", TARGET_FOLDER,
                absFilePath);
        if (status != 0) {
            throw new RuntimeException(absFilePath + "文件编译失败");
        }
        String clazzFile = TARGET_FOLDER + javaFilePath + ".class";
        try (FileInputStream fileInputStream = new FileInputStream(new File(clazzFile))) {
            File file = new File(clazzFile);
            int length = Long.valueOf(file.length()).intValue();
            byte[] bytes = new byte[length];
            IOUtils.readFully(fileInputStream, bytes);
            Class<?> res = defineClass(name, bytes, 0, length);
            resolveClass(res);
            cache.put(name, res);
            return res;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(clazzFile + " not found");
        }
    }

    public static List<String> resolveLib() {
        Document document = XmlUtil.readXML(new File("D:\\campusshare\\campusshare.iml"));
        Element rootElement = XmlUtil.getRootElement(document);
        NodeList orderEntry = document.getElementsByTagName("orderEntry");
        List<String> res = new ArrayList<>();
        for (int i = 0; i < orderEntry.getLength(); i++) {
            Node item = orderEntry.item(i);
            String name = ((DeferredElementImpl) item).getAttribute("name");
            if (StringUtils.isNotBlank(name)) {
                name = name.replace("Maven: ", "");
                res.add(getLibAbsPath(name));
            }
        }
        return res;
    }

    /**
     * @param libName Maven: org.springframework.boot:spring-boot-starter-data-jpa:2.1.7.RELEASE
     * @return D:\develop\apache-maven-3.6.3\local-repo\org\springframework\boot\spring-boot-starter-data-jpa\2.1.7.RELEASE\spring-boot-starter-data-jpa-2.1.7.RELEASE.jar
     */
    private static String getLibAbsPath(String libName) {
        String[] split = StringUtils.split(libName, ":");
        String groupId = split[0];
        String artifactId = split[1];
        String version = split[2];
        String jarFileAbsPath = MAVEN_REPO + packageToPath(groupId) + "\\" + artifactId + "\\" + version + "\\" + artifactId + "-" + version + ".jar";
        return jarFileAbsPath;
    }

    private static String packageToPath(String pkg) {
        String res = pkg;
        while (res.contains(".")) {
            res = res.replace(".", "\\");
        }
        return res;
    }

}
